﻿"""FastAPI dependency functions.

IMPORTANT: 本项目统一使用同步Session依赖注入
"""

from __future__ import annotations

import logging
from typing import Callable

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from fastapi import WebSocket
from jose import JWTError, jwt
from sqlalchemy.orm import Session

from ..core.config import settings
from ..db.session import get_db
from ..models.user import User
from ..repositories.user_repository import UserRepository

logger = logging.getLogger(__name__)

# Explicit exports for mypy
__all__ = ["get_db", "get_current_user"]

# New repositories
from ..repositories.lead_repository import LeadRepository
from ..repositories.lead_contact_repository import LeadContactRepository
from ..repositories.lead_note_repository import LeadNoteRepository
from ..repositories.import_batch_repository import ImportBatchRepository
from ..repositories.import_record_repository import ImportRecordRepository
from ..repositories.lead_ai_summary_repository import LeadAISummaryRepository
from ..repositories.property_repository import PropertyRepository
from ..repositories.visitor_sheet_repository import VisitorSheetRepository

# New services
from ..services.lead_service import LeadService
from ..services.lead_import_service import LeadImportService
from ..services.ai_summary_service import AISummaryService
from ..services.stats_service import StatsService
from ..services.visitor_sheet_service import VisitorSheetService
from ..services.ai_tag_engine import HybridTagEngine
from ..services.ai_extraction_service import AIExtractionService
from ..services.property_match_service import PropertyMatchService


oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login")


def get_session() -> Session:
    """
    返回同步Session（已废弃，建议直接使用get_db）

    为了向后兼容保留此函数，新代码请直接使用 `db: Session = Depends(get_db)`
    """
    return next(get_db())


# Repositories DI
def get_lead_repository(session: Session = Depends(get_db)) -> LeadRepository:
    return LeadRepository(session)


def get_lead_contact_repository(session: Session = Depends(get_db)) -> LeadContactRepository:
    return LeadContactRepository(session)


def get_lead_note_repository(session: Session = Depends(get_db)) -> LeadNoteRepository:
    return LeadNoteRepository(session)


def get_import_batch_repository(session: Session = Depends(get_db)) -> ImportBatchRepository:
    return ImportBatchRepository(session)


def get_lead_ai_summary_repository(session: Session = Depends(get_db)) -> LeadAISummaryRepository:
    return LeadAISummaryRepository(session)


# Services DI
def get_lead_service(
    session: Session = Depends(get_db),
) -> LeadService:
    return LeadService(
        lead_repo=LeadRepository(session),
        contact_repo=LeadContactRepository(session),
        note_repo=LeadNoteRepository(session),
        ai_repo=LeadAISummaryRepository(session),
    )


# 单例缓存：AIEngine（无状态，可复用）
_ai_engine_singleton: HybridTagEngine | None = None


def _get_ai_engine_singleton() -> HybridTagEngine | None:
    """获取AI引擎单例（优化性能，避免每次请求创建）."""
    global _ai_engine_singleton

    if _ai_engine_singleton is not None:
        return _ai_engine_singleton

    # 只初始化一次
    qwen_key = settings.QWEN_API_KEY or settings.DASHSCOPE_API_KEY
    hunyuan_id = getattr(settings, "HUNYUAN_SECRET_ID", None)
    hunyuan_key = getattr(settings, "HUNYUAN_SECRET_KEY", None)

    if qwen_key or (hunyuan_id and hunyuan_key):
        _ai_engine_singleton = HybridTagEngine(
            qwen_api_key=qwen_key,
            hunyuan_secret_id=hunyuan_id,
            hunyuan_secret_key=hunyuan_key,
            cache_size=2000,
        )
        logger.info("AI Engine singleton initialized successfully")
    else:
        logger.warning("AI Engine not initialized - no API keys configured")

    return _ai_engine_singleton


def get_import_service(
    session: Session = Depends(get_db),
) -> LeadImportService:
    # 使用单例AIEngine（性能优化）
    ai_engine = _get_ai_engine_singleton()

    # 初始化TagService（用于标签匹配和关联）
    from ..repositories.tag_repository import TagRepository
    from ..repositories.lead_tag_repository import LeadTagRepository
    from ..services.tag_service import TagService

    # 获取AI配置（与AI引擎使用相同的配置）
    qwen_key = settings.QWEN_API_KEY or settings.DASHSCOPE_API_KEY
    hunyuan_id = getattr(settings, "HUNYUAN_SECRET_ID", None)
    hunyuan_key = getattr(settings, "HUNYUAN_SECRET_KEY", None)

    tag_repo = TagRepository(session)
    lead_tag_repo = LeadTagRepository(session)
    tag_service = TagService(
        tag_repo=tag_repo,
        lead_tag_repo=lead_tag_repo,
        qwen_api_key=qwen_key,
        hunyuan_secret_id=hunyuan_id,
        hunyuan_secret_key=hunyuan_key,
    )

    return LeadImportService(
        batch_repo=ImportBatchRepository(session),
        record_repo=ImportRecordRepository(session),
        lead_repo=LeadRepository(session),
        ai_engine=ai_engine,
        tag_service=tag_service,
    )


def get_ai_summary_service(
    session: Session = Depends(get_db),
) -> AISummaryService:
    return AISummaryService(
        ai_repo=LeadAISummaryRepository(session),
        lead_repo=LeadRepository(session),
    )


def get_stats_service(
    session: Session = Depends(get_db),
) -> StatsService:
    return StatsService(
        lead_repo=LeadRepository(session),
        property_repo=PropertyRepository(session),
        visitor_sheet_repo=VisitorSheetRepository(session),
    )


def get_visitor_sheet_service(
    session: Session = Depends(get_db),
) -> VisitorSheetService:
    return VisitorSheetService(visitor_sheet_repo=VisitorSheetRepository(session))


# AI Extraction Service (单例)
_ai_extraction_singleton: AIExtractionService | None = None


def _get_ai_extraction_singleton() -> AIExtractionService:
    """获取AI提取服务单例."""
    global _ai_extraction_singleton

    if _ai_extraction_singleton is not None:
        return _ai_extraction_singleton

    # 使用与AI引擎相同的配置
    qwen_key = settings.QWEN_API_KEY or settings.DASHSCOPE_API_KEY

    _ai_extraction_singleton = AIExtractionService(qwen_api_key=qwen_key)
    logger.info("AI Extraction Service singleton initialized successfully")

    return _ai_extraction_singleton


def get_ai_extraction_service() -> AIExtractionService:
    """获取AI提取服务（公共依赖注入）."""
    return _get_ai_extraction_singleton()


def get_property_match_service(
    session: Session = Depends(get_db),
) -> PropertyMatchService:
    """获取房源匹配服务."""
    return PropertyMatchService(
        lead_repo=LeadRepository(session),
        property_repo=PropertyRepository(session),
        ai_extraction=_get_ai_extraction_singleton(),
    )


def get_current_user(
    token: str = Depends(oauth2_scheme),
    session: Session = Depends(get_db),
) -> User:
    """获取当前登录用户（同步版本）"""
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
    except JWTError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token"
        ) from exc
    user_id = payload.get("sub")
    if user_id is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token payload"
        )
    repo = UserRepository(session)
    try:
        user = repo.get_by_id(int(user_id))
    except ValueError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid subject type"
        ) from exc
    if user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
    if not user.is_active:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User disabled")
    return user


def require_roles(*roles: str) -> Callable[[User], User]:
    def _dependency(current_user: User = Depends(get_current_user)) -> User:
        if current_user.role not in roles:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Insufficient role")
        return current_user

    return _dependency


async def get_current_user_ws(websocket: WebSocket, session: Session = Depends(get_db)) -> User:
    """
    Authenticate WebSocket connections using `?token` query parameter.

    Note: WebSocket本身需要async（await websocket.close()），数据库操作在threadpool中执行
    """
    from starlette.concurrency import run_in_threadpool

    token = websocket.query_params.get("token")
    if not token:
        await websocket.close(code=4401)
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token required")
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
    except JWTError:
        await websocket.close(code=4401)
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
    user_id = payload.get("sub")
    if user_id is None:
        await websocket.close(code=4401)
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token payload"
        )

    # 在threadpool中执行同步数据库操作，避免阻塞事件循环
    repo = UserRepository(session)
    user = await run_in_threadpool(repo.get_by_id, int(user_id))

    if user is None or not user.is_active:
        await websocket.close(code=4401)
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
    return user
